package com.tan.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tan.common.domain.user.SysRole;
import com.tan.common.dto.user.RoleCreateDto;
import com.tan.common.dto.user.RoleMenuDTO;
import com.tan.common.dto.user.RoleUpdateDto;
import com.tan.common.mapper.user.SysRoleMapper;
import com.tan.common.tools.ConvertTool;
import com.tan.common.mapstruct.user.RoleConvert;
import com.tan.common.query.user.RoleQuery;
import com.tan.common.tools.PageTool;
import com.tan.common.vo.user.RoleVO;
import com.tan.core.enums.RedisKeyEnum;
import com.tan.core.utils.*;
import com.tan.user.service.ISysRoleMenuService;
import com.tan.user.service.ISysRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tan.user.service.ISysUserRoleService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author Tan
 * @since 2025-01-27
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {

    @Resource
    private ISysUserRoleService userRoleService;

    @Resource
    private ISysRoleMenuService roleMenuService;

    @Resource
    private RedissonTool redissonTool;

    @Resource
    private ThreadPoolTool threadPoolTool;

    @Override
    public IPage<RoleVO> rolePages(RoleQuery query) {
        LambdaQueryWrapper<SysRole> wrapper = Wrappers.lambdaQuery(SysRole.class);
        wrapper.eq(SysRole::getDelFlag, false);
        wrapper.eq(query.getStatus() != null, SysRole::getStatus, query.getStatus());
        wrapper.like(StringUtils.isNotBlank(query.getKeyword()), SysRole::getName, query.getKeyword());
        wrapper.like(StringUtils.isNotBlank(query.getKeyword()), SysRole::getCode, query.getKeyword());
        IPage<SysRole> pages = this.baseMapper.selectPage(new Page<>(query.getCurrent(), query.getSize()), wrapper);
        return PageTool.convert(pages, RoleConvert.class);
    }

    @Override
    public List<RoleVO> roles() {
        LambdaQueryWrapper<SysRole> wrapper = Wrappers.lambdaQuery(SysRole.class);
        wrapper.eq(SysRole::getDelFlag, false);
        wrapper.eq( SysRole::getStatus, true);
        List<SysRole> roles = this.baseMapper.selectList( wrapper);
        return RoleConvert.INSTANCE.toTargets(roles);
    }

    @Override
    public void createRole(RoleCreateDto roleCreateDto) {
        SysRole role = RoleConvert.INSTANCE.dtoDomain(roleCreateDto);
        if (StringUtils.isBlank(role.getCode())) {
            role.setCode(IdGenerate.fastSimpleUUID());
        }
        ExceptionTool.isNullError(role, SysRole::getName, "角色名必填");
        int count = this.baseMapper.insert(role);
        ExceptionTool.operateSqlError(count, "创建角色失败");
    }

    @Override
    public void updateRole(RoleUpdateDto roleUpdateDto) {
        SysRole role = RoleConvert.INSTANCE.dtoDomain(roleUpdateDto);
        int count = this.baseMapper.updateById(role);
        ExceptionTool.operateSqlError(count, "修改角色失败");
        this.clearRoleCache(role.getId());
    }

    @Override
    public void delRole(Long roleId) {
        SysRole role = this.baseMapper.selectById(roleId);
        ExceptionTool.isNullError(role, "此角色不存在");
        role.setDelFlag(true);
        this.baseMapper.updateById(role);
        this.clearRoleCache(roleId);
    }


    @Override
    public List<RoleVO> userRoles(Long userId) {
        String key = RedisKeyEnum.USER_ROLES.createKey(userId);
        List<RoleVO> roleVOS = redissonTool.getObject(key);
        if (roleVOS != null) {
            return roleVOS;
        }
        List<Long> roles = userRoleService.rolesByUser(userId);
        if (roles.isEmpty()) return Collections.emptyList();
        List<SysRole> roles1 = this.baseMapper.selectList(Wrappers.lambdaQuery(SysRole.class)
                .eq(SysRole::getDelFlag, false)
                .eq(SysRole::getStatus, true).in(SysRole::getId, roles));
        roleVOS = ConvertTool.toTarget(RoleConvert.class, roles1);
        redissonTool.cacheObjWeek(key, roleVOS);
        return roleVOS;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void allocationMenu(RoleMenuDTO roleMenuDTO) {
        this.roleMenuService.delByRole(roleMenuDTO.getRoleId());
        this.roleMenuService.insertBatch(roleMenuDTO);
        this.clearRoleCache(roleMenuDTO.getRoleId());
    }

    @Override
    public void clearRoleCache(Long roleId) {
        threadPoolTool.execute(() -> {
            List<Long> users = userRoleService.usersByRole(roleId);
            users.forEach(this::clearUserRole);
        });
    }

    @Override
    public void clearRoleCache(List<Long> ids) {
        threadPoolTool.execute(() -> {
            Set<Long> users = userRoleService.usersByRole(ids);
            users.forEach(this::clearUserRole);
        });
    }

    @Override
    public void clearUserRole(Long userId) {
        String rolesKey = RedisKeyEnum.USER_ROLES.createKey(userId);
        String routeKey = RedisKeyEnum.USER_ROUTE.createKey(userId);
        redissonTool.delCache(rolesKey);
        redissonTool.delCache(routeKey);
    }
}
